package cn.com.connext.scrm.utils.wechat;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.WechatPayUploadHttpPost;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.SneakyThrows;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
public class WeiXinUtils {

    private static Logger logger = LoggerFactory.getLogger(WeiXinUtils.class);
    private static final Map<String, Certificate> CERTIFICATE_MAP = new ConcurrentHashMap<>();
    private static final String  apiV3Key = "20210805A161000ADiageoOmata0417y";
    private static final String  merchantSerialNumber = "32075FA334FA59896AEF7879670E82AB13075120";
    private static final String  merchantId = "1612505750";

    @Autowired
    private static RestTemplate restTemplate = new RestTemplate();

    public static String getJsApiTicket(String accessToken, String type) {
        String jsApiTicket = "";
        // 这个url链接地址和参数皆不能变
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=" + type;
        try {
            JSONObject jsonObject = restTemplate.getForObject(url, JSONObject.class);
            logger.info("获取jsapi_ticket返回的json={}", jsonObject);
            Long expiresIn = jsonObject.getLong("expires_in");
            //String ticket = jsonObject.getString("ticket");
            jsApiTicket = jsonObject.getString("ticket");
            logger.info("微信获取的ticket={}" + jsApiTicket);
        } catch (Exception e) {
            logger.debug("获取jsapi_ticket发生异常", e);
        }
        return jsApiTicket;
    }


    /**
     * 构造验签名串.
     *
     * @param wechatpayTimestamp HTTP头 Wechatpay-Timestamp 中的应答时间戳。
     * @param wechatpayNonce     HTTP头 Wechatpay-Nonce 中的应答随机串
     * @param body               响应体
     * @return the string
     */
    public String createSign(String wechatpayTimestamp, String wechatpayNonce, String body) {
        return Stream.of(wechatpayTimestamp, wechatpayNonce, body)
            .collect(Collectors.joining("\n", "", "\n"));
    }
    /**
     * 我方对响应验签，和应答签名做比较，使用微信平台证书.
     *
     * @param wechatpaySerial    response.headers['Wechatpay-Serial']    当前使用的微信平台证书序列号
     * @param wechatpaySignature response.headers['Wechatpay-Signature']   微信平台签名
     * @param wechatpayTimestamp response.headers['Wechatpay-Timestamp']  微信服务器的时间戳
     * @param wechatpayNonce     response.headers['Wechatpay-Nonce']   微信服务器提供的随机串
     * @param body               response.body 微信服务器的响应体
     * @return the boolean
     */
    @SneakyThrows
    public boolean responseSignVerify(String wechatpaySerial, String wechatpaySignature, String wechatpayTimestamp, String wechatpayNonce, String body) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {

        if (CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) {
            try {
                refreshCertificate();
            } catch (CertificateException e) {
                e.printStackTrace();
            }
        }
        Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial);

        final String signatureStr = createSign(wechatpayTimestamp, wechatpayNonce, body);
        Signature signer = Signature.getInstance("SHA256withRSA");
        signer.initVerify(certificate);
        signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));

        return signer.verify(Base64Utils.decodeFromString(wechatpaySignature));
    }

//    public static void main(String[] args) {
//        ObjectMapper objectMapper = new ObjectMapper();
//        String wxHttpClient = getWxHttpClient("https://api.mch.weixin.qq.com/v3/certificates", objectMapper, objectMapper.createObjectNode());
//        System.out.println(wxHttpClient);
//    }

    public void refreshCertificate() throws CertificateException {
        ObjectMapper objectMapper = new ObjectMapper();
        String wxHttpClient = this.getWxHttpClient("https://api.mch.weixin.qq.com/v3/certificates", objectMapper, objectMapper.createObjectNode());


        if (StringUtils.isBlank(wxHttpClient)) {
            logger.info("调用证书接口失败");
            return;
        }
        JSONObject jsonObject = JSONObject.parseObject(wxHttpClient);
        JSONArray data = jsonObject.getJSONArray("data");
        if (data.size()>0) {
            // 清理HashMap
            CERTIFICATE_MAP.clear();
        }
        for (int i = 0; i < data.size(); i++) {
            JSONObject jsonObject1 = data.getJSONObject(i);

            JSONObject encrypt_certificate = jsonObject1.getJSONObject("encrypt_certificate");
            String associatedData = encrypt_certificate.getString("associated_data");
            String nonce = encrypt_certificate.getString("nonce");
            String ciphertext = encrypt_certificate.getString("ciphertext");

            String publicKey = decryptResponseBody(associatedData, nonce, ciphertext);
            logger.info("publicKey ={} ",publicKey);
            final CertificateFactory cf = CertificateFactory.getInstance("X509");

            ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8));
            Certificate certificate = null;
            try {
                certificate = cf.generateCertificate(inputStream);
            } catch (CertificateException e) {
                e.printStackTrace();
            }
            String responseSerialNo = jsonObject1.getString("serial_no");

            // 放入证书
            CERTIFICATE_MAP.put(responseSerialNo, certificate);
        }

    }
//    private static final String ALGORITHM = "AES/GCM/NoPadding";
//    private static final int TAG_LENGTH_BIT = 128;
//    private static final int NONCE_LENGTH_BYTE = 12;
//    private static final String AES_KEY = ""; // APIv3密钥
//    private static final String TRANSFORMATION_PKCS1Padding = "RSA/ECB/PKCS1Padding";
//    private static String aesgcmDecrypt(String aad, String iv, String cipherText) throws Exception {
//        final Cipher cipher = Cipher.getInstance(ALGORITHM, "SunJCE");
//        SecretKeySpec key = new SecretKeySpec(AES_KEY.getBytes(), "AES");
//        GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, iv.getBytes());
//        cipher.init(Cipher.DECRYPT_MODE, key, spec);
//        cipher.updateAAD(aad.getBytes());
//        return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)));
//    }
//    public static void main(String[] args) {
//        final String associatedData = ""; // encrypt_certificate.associated_data
//        final String nonce = ""; // encrypt_certificate.nonce
//        final String cipherText = ""; // encrypt_certificate.ciphertext
//        try {
//            String wechatpayCert = aesgcmDecrypt(associatedData, nonce, cipherText);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

    /**
     * 解密响应体.
     *
     * @param// apiV3Key       API V3 KEY  API v3密钥 商户平台设置的32位字符串
     * @param associatedData  response.body.data[i].encrypt_certificate.associated_data
     * @param nonce          response.body.data[i].encrypt_certificate.nonce
     * @param ciphertext     response.body.data[i].encrypt_certificate.ciphertext
     * @return the string
     * @throws GeneralSecurityException the general security exception
     */
    public String decryptResponseBody(String associatedData, String nonce, String ciphertext) {
        try {
//            apiV3Key = "20210805A161000ADiageoOmata0417y";
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));

            byte[] bytes;
            try {
                bytes = cipher.doFinal(Base64Utils.decodeFromString(ciphertext));
            } catch (GeneralSecurityException e) {
                throw new IllegalArgumentException(e);
            }
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }
    /**
     * post 上传微信文件
     * @return
     */
    public String uploadHttp(String filePath){
        //序列号
//        String merchantSerialNumber = "32075FA334FA59896AEF7879670E82AB13075120";
        //商户号
//        String merchantId = "1612505750";
//        String apiV3Key = "20210805A161000ADiageoOmata0417y";
        //私钥
        PrivateKey merchantPrivateKey = null;
        WechatPayHttpClientBuilder builder = null;
        String bodyAsString = null;
        try {
            merchantPrivateKey = PemUtil.loadPrivateKey(
                new FileInputStream(File.separator+"etc"+File.separator+"apiclient_key.pem"));
            //不需要传入微信支付证书了 需要v3Key
            AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(merchantId, new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
                apiV3Key.getBytes("utf-8"));
            builder = WechatPayHttpClientBuilder.create()
                .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));
            HttpClient httpClient = builder.build();
            URI uri = new URI("https://api.mch.weixin.qq.com/v3/marketing/favor/media/image-upload");
//            String filePath = "D://微信图片_20180914095848.jpg";
            File file = new File(filePath);

            try (FileInputStream ins1 = new FileInputStream(file)) {
                String sha256 = DigestUtils.sha256Hex(ins1);
                try (InputStream ins2 = new FileInputStream(file)) {
                    HttpPost request = new WechatPayUploadHttpPost.Builder(uri)
                        .withImage(file.getName(), sha256, ins2)
                        .build();
                    CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(request);
                    HttpEntity entity = response.getEntity();
                    bodyAsString = EntityUtils.toString(entity);
                }
            }


        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return bodyAsString;
    }
    /**
     * post httpClient请求微信
     * @param url
     * @param objectMapper
     * @param rootNode
     * @return
     */
    public String postWxHttpClient(String url,ObjectMapper objectMapper,ObjectNode rootNode){
        //序列号
//        String merchantSerialNumber = "32075FA334FA59896AEF7879670E82AB13075120";
        //商户号
//        String merchantId = "1612505750";
//        String apiV3Key = "20210805A161000ADiageoOmata0417y";
        //私钥
        PrivateKey merchantPrivateKey = null;
        WechatPayHttpClientBuilder builder = null;
        String bodyAsString = null;
        try {
            merchantPrivateKey = PemUtil.loadPrivateKey(
                new FileInputStream(File.separator+"etc"+File.separator+"apiclient_key.pem"));
            //不需要传入微信支付证书了 需要v3Key
            AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(merchantId, new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
                apiV3Key.getBytes("utf-8"));
            builder = WechatPayHttpClientBuilder.create()
                .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));
            HttpClient httpClient = builder.build();
            HttpPost httpPost = new HttpPost(url);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type","application/json; charset=utf-8");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
//            ObjectMapper objectMapper = new ObjectMapper();
//            ObjectNode rootNode = objectMapper.createObjectNode();
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost);
            bodyAsString = EntityUtils.toString(response.getEntity());


        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bodyAsString;
    }

    /**
     * get httpClient请求微信
     * @param url
     * @param objectMapper
     * @param rootNode
     * @return
     */
    public String getWxHttpClient(String url, ObjectMapper objectMapper, ObjectNode rootNode){
        //序列号
//        String merchantSerialNumber = "32075FA334FA59896AEF7879670E82AB13075120";
        //商户号
//        String merchantId = "1612505750";
//        String apiV3Key = "20210805A161000ADiageoOmata0417y";
        //私钥
        PrivateKey merchantPrivateKey = null;
        WechatPayHttpClientBuilder builder = null;
        String bodyAsString = null;
        try {
            merchantPrivateKey = PemUtil.loadPrivateKey(
                new FileInputStream(File.separator+"etc"+File.separator+"apiclient_key.pem"));
            //不需要传入微信支付证书了 需要v3Key
            AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(merchantId, new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
                apiV3Key.getBytes("utf-8"));
            builder = WechatPayHttpClientBuilder.create()
                .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));
            HttpClient httpClient = builder.build();
            HttpGet httpPost = new HttpGet(url);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type","application/json; charset=utf-8");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
//            ObjectMapper objectMapper = new ObjectMapper();
//            ObjectNode rootNode = objectMapper.createObjectNode();
//            HttpGet.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost);
            try {
                objectMapper.writeValue(bos, rootNode);
                bodyAsString = EntityUtils.toString(response.getEntity());
                return bodyAsString;
            } catch (IOException e) {
                e.printStackTrace();
            }

        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bodyAsString;

    }
}
